package demo;

import cn.hutool.core.date.DateUtil;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author： zidong.gao@luckincoffee.com
 * @date： 2022-05-31 15:19
 * @description： 更新日志对比工具类
 */
@Slf4j
public class ComparisonLogUtil {
    private static final String TRANSLATE_METHOD = "getNameByIndex";
    private static final String NULL_STRING = "null";
    private static final String SEMICOLON = "；";
    private static final String COLON = "：";
    private static final String ARROW = ">>";

    /**
     * 更新日志使用，对比更新前后的值
     * 实体类需要对比的字段加入@ValueComparison注解
     * oldObj和newObj需要为同一类型
     *
     * @param oldObj                更新前的实体类
     * @param newObj                更新后的实体类
     * @param ignoreNewObjNullValue 是否忽略“更新后实体类”对象空值字段
     * @return 更新日志
     */
    public static String compareUpdateInfo(Object oldObj, Object newObj, boolean ignoreNewObjNullValue) {
        Class<?> clazz = oldObj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        if (!Objects.equals(clazz.getName(), newObj.getClass().getName())) {
            log.error("更新日志对比的两个对象类型不一致");
            throw new RuntimeException("更新日志生成错误");
        }
        StringBuilder content = new StringBuilder();
        try {
            for (Field field : fields) {
                if (!field.isAnnotationPresent(ValueComparison.class)) {
                    continue;
                }
                field.setAccessible(true);
                ValueComparison valueComparison = field.getAnnotation(ValueComparison.class);
                Object newFieldValue = field.get(newObj);
                if (ignoreNewObjNullValue && newFieldValue == null) {
                    continue;
                }
                Object oldFieldValue = field.get(oldObj);
                if (Objects.equals(oldFieldValue, newFieldValue)) {
                    continue;
                }
                content.append(valueComparison.fieldName()).append(COLON);
                boolean needToTranslate = valueComparison.enumClazz().isEnum();
                if (needToTranslate) {
                    Method method = valueComparison.enumClazz().getMethod(TRANSLATE_METHOD, Integer.class);
                    content.append(invokeMethod(oldFieldValue, method, valueComparison)).append(ARROW).
                            append(invokeMethod(newFieldValue, method, valueComparison)).append(SEMICOLON);
                } else {
                    // 日期转换
                    if (newFieldValue instanceof Date) {
                        newFieldValue = DateUtil.format((Date) newFieldValue, valueComparison.datePattern());
                    }
                    if (oldFieldValue instanceof Date) {
                        oldFieldValue = DateUtil.format((Date) oldFieldValue, valueComparison.datePattern());
                    }
                    content.append(oldFieldValue).append(ARROW).append(newFieldValue).append(SEMICOLON);
                }
            }
        } catch (Exception e) {
            log.error("更新日志生成错误", e);
            throw new RuntimeException("更新日志生成错误");
        }
        return String.valueOf(content);
    }

    private static Object invokeMethod(Object obj, Method method, ValueComparison valueComparison) {
        if (null == obj) {
            return NULL_STRING;
        }
        try {
            return method.invoke(valueComparison.enumClazz(), obj);
        } catch (Exception e) {
            log.error("执行" + valueComparison.enumClazz() + method.getName() + "出错", e);
            throw new RuntimeException("执行" + valueComparison.enumClazz() + method.getName() + "出错");
        }
    }

    /**
     * 对比更新选中的字段日志
     * 需注意：bigdecimal类型字段的比较
     *
     * @param oldObj
     * @param newObj
     * @param comparedFields
     * @return
     */
    public static String compareChosenField(Object oldObj, Object newObj, List<String> comparedFields) {
        Class<?> clazz = oldObj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        StringBuilder content = new StringBuilder();
        try {
            for (Field field : fields) {
                if (!comparedFields.contains(field.getName())) {
                    continue;
                }
                field.setAccessible(true);
                ValueComparison valueComparison = field.getAnnotation(ValueComparison.class);
                Object oldFieldValue = field.get(oldObj);
                Object newFieldValue = field.get(newObj);
                if (Objects.equals(oldFieldValue, newFieldValue)) {
                    continue;
                }
                content.append(valueComparison.fieldName()).append(COLON);
                content.append(oldFieldValue).append(ARROW).append(newFieldValue).append(SEMICOLON);
            }
        } catch (Exception e) {
            log.error("更新日志生成错误", e);
            return "";
        }
        return String.valueOf(content);
    }
}
